/*
 * \brief  Core main program
 * \author Norman Feske
 * \date   2006-07-12
 */

/*
 * Copyright (C) 2006-2009 Norman Feske
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

/* Genode */
#include <base/snprintf.h>
#include <base/sleep.h>
#include <rom_session/client.h>

/* Core */
#include "platform.h"
#include "core_env.h"
#include "core_child.h"

#include "ram_root.h"
#include "rom_root.h"
#include "cap_root.h"
#include "rm_root.h"
#include "cpu_root.h"
#include "pd_root.h"
#include "log_root.h"
#include "io_mem_root.h"
#include "io_port_root.h"
#include "irq_root.h"

using namespace Genode;


/* support for cap session component */
long Cap_session_component::_unique_id_cnt;
Lock Cap_session_component::_lock;

/* pool of provided core services */
static Local_service_pool local_services;


/***************************************
 ** Core environment/platform support **
 ***************************************/

Core_env * Genode::core_env()
{
	/*
	 * By placing the environment as static object here, we ensure that its
	 * constructor gets called when this function is used the first time.
	 */
	static Core_env _env;
	return &_env;
}


Env * Genode::env() {
	return core_env(); }


Platform_generic * Genode::platform()
{
	static Platform _platform;
	return &_platform;
}


/*************************
 ** Core parent support **
 *************************/

Session_capability Core_parent::session(const char *service_name, const char *args)
{
	Local_service *ls = local_services.find(service_name);

	if (ls) return ls->root()->session(args);

	PWRN("service_name=\"%s\" arg=\"%s\" not handled", service_name, args);
	return Session_capability();
}


/***************
 ** Core main **
 ***************/

int main()
{
	PDBG("--- create local services ---");

	/*
	 * Initialize root interfaces for our services
	 */
	Server_entrypoint *e = core_env()->entrypoint();

	/*
	 * Allocate session meta data on distinct dataspaces to enable independent
	 * destruction (to enable quota trading) of session component objects.
	 */
	static Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session());

	static Cap_root     cap_root     (e, &sliced_heap);
	static Ram_root     ram_root     (e, e, platform()->ram_alloc(), &sliced_heap);
	static Rom_root     rom_root     (e, e, platform()->rom_fs(), &sliced_heap);
	static Rm_root      rm_root      (e, e, e, &sliced_heap,
	                                  platform()->vm_start(), platform()->vm_size());
	static Cpu_root     cpu_root     (e, e, &sliced_heap);
	static Pd_root      pd_root      (e, e, &sliced_heap);
	static Log_root     log_root     (e, &sliced_heap);
	static Io_mem_root  io_mem_root  (e, e, platform()->io_mem_alloc(),
	                                  platform()->ram_alloc(), &sliced_heap);
	static Io_port_root io_port_root (e, platform()->io_port_alloc(), &sliced_heap);
	static Irq_root     irq_root     (core_env()->cap_session(),
	                                  platform()->irq_alloc(), &sliced_heap);

	/*
	 * Play our role as parent of init and declare our services.
	 */

	static Local_service ls[] = {
		Local_service(Rom_session::service_name(),     &rom_root),
		Local_service(Ram_session::service_name(),     &ram_root),
		Local_service(Cap_session::service_name(),     &cap_root),
		Local_service(Rm_session::service_name(),      &rm_root),
		Local_service(Cpu_session::service_name(),     &cpu_root),
		Local_service(Pd_session::service_name(),      &pd_root),
		Local_service(Log_session::service_name(),     &log_root),
		Local_service(Io_mem_session::service_name(),  &io_mem_root),
		Local_service(Io_port_session::service_name(), &io_port_root),
		Local_service(Irq_session::service_name(),     &irq_root)
	};

	/* make our local services known to service pool */
	for (unsigned i = 0; i < sizeof(ls) / sizeof(Local_service); i++)
		local_services.insert(&ls[i]);

	PDBG("--- start init ---");

	/*
	 * Obtain dataspace with init binary and start it.
	 */
	Rom_session_capability init_rom_session_cap(rom_root.session("filename=init, ram_quota=4K"));
	if (!init_rom_session_cap.valid()) {
		PERR("ROM for \"init\" not found!");
		return -1;
	}

	/* create ram session for init and transfer some of our own quota */
	Ram_session_capability init_ram_session_cap(ram_root.session("ram_quota=4K"));
	Ram_session_client(init_ram_session_cap).ref_account(env()->ram_session_cap());

	/* transfer all left memory to init, but leave 1 MB left for core */
	size_t init_quota = platform()->ram_alloc()->avail() - 1024*1024;
	env()->ram_session()->transfer_quota(init_ram_session_cap, init_quota);
	PDBG("Ok we have %d MB transfered to init", init_quota / (1024*1024));

	Dataspace_capability ds_cap = Rom_session_client(init_rom_session_cap).dataspace();
	Cpu_session_capability init_cpu_session_cap(cpu_root.session("ram_quota=4K"));

	Core_child *init = new (env()->heap())
	                   Core_child("init", ds_cap, init_ram_session_cap,
	                              init_cpu_session_cap,
	                              core_env()->cap_session(), &local_services);
	init->finalize_construction();

	PDBG("--- init created, waiting for exit condition ---");
	platform()->wait_for_exit();

	PDBG("--- destroying init ---");
	destroy(env()->heap(), init);

	rom_root.close(init_rom_session_cap);
	ram_root.close(init_ram_session_cap);
	cpu_root.close(init_cpu_session_cap);

	PDBG("--- core main says good bye ---");

	/*
	 * Call destructor of Core environment manually.  We instanciate the Core
	 * environment as a static variable within the scope of the core_env()
	 * function. Such objects get registered in runtime via the function
	 * __cxa_atexit. In our cxx support lib however, we only provide a dummy
	 * function for __cxa_atexit. Therefore, the Core environment object gets
	 * not registered for destruction and we take care of it by ourself.
	 */
	core_env()->~Core_env();
	return 0;
}
